/*
 * =====================================================================================
 *
 *       Filename:  uart.cpp
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  07/03/15 10:03:35
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Andy (gk), andy_y_li@163.com
 *        Company:  RD
 *
 * =====================================================================================
 */

#include "uart.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "strRegex.h"
#include "timeOut.h"
#include "CString.h"

//====================
#if 0
unsigned int
rs232_open(struct rs232_port_t *p)
{
	int flags;
	struct termios term;
	struct rs232_posix_t *ux = p->pt;

	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);

	ux->fd = open(p->dev, O_RDWR | O_NOCTTY | O_NDELAY);
	if (ux->fd < 0) {
		DBG("open() %d %s\n", errno, strerror(errno))
		return RS232_ERR_OPEN;
	}

	/* 
	 * On OSX (and maybe on more systems), we need to open() the port with
	 * O_NDELAY, because otherwise it would block forever waiting for DCD
	 * signal, so here we restore back to blocking operations.
	 */
	flags = fcntl(ux->fd, F_GETFL);
	flags &= ~O_NDELAY;
	fcntl(ux->fd, F_SETFL, flags);

	if (tcflush(ux->fd, TCIOFLUSH) < 0) {
		DBG("tcflush() %d %s\n", errno, strerror(errno))
		return RS232_ERR_CONFIG;
	}

	GET_PORT_STATE(ux->fd, &term)
	GET_PORT_STATE(ux->fd, &ux->oldterm)

	term.c_cflag |= (CREAD | CLOCAL);
	term.c_iflag = IGNPAR;
	term.c_oflag = 0;
	term.c_lflag = 0;
	term.c_cc[VINTR]  = _POSIX_VDISABLE;
	term.c_cc[VQUIT]  = _POSIX_VDISABLE;
	term.c_cc[VSTART] = _POSIX_VDISABLE;
	term.c_cc[VSTOP]  = _POSIX_VDISABLE;
	term.c_cc[VSUSP]  = _POSIX_VDISABLE;
	term.c_cc[VEOF]   = _POSIX_VDISABLE;
	term.c_cc[VEOL]   = _POSIX_VDISABLE;
	term.c_cc[VERASE] = _POSIX_VDISABLE;
	term.c_cc[VKILL]  = _POSIX_VDISABLE;

	SET_PORT_STATE(ux->fd, &term)

	rs232_set_baud(p, p->baud);
	rs232_set_data(p, p->data);
	rs232_set_parity(p, p->parity);
	rs232_set_stop(p, p->stop);
	rs232_set_flow(p, p->flow);
	p->status = RS232_PORT_OPEN;

	return RS232_ERR_NOERROR;
}
#endif

//====================

Uart::Uart(const char *port, int baudRate)
{
    this->readBuffer = NULL;
    mymutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
    buffer = (char *)malloc(FIFO_MAX_SIZE * sizeof(char));
    if (buffer == NULL) {
        cout << "Error on malloc: " << strerror(errno) << endl;
        return;
    }
    memset(buffer, 0, FIFO_MAX_SIZE * sizeof(char));
    fifo_init(&fifoBuffer, FIFO_MAX_SIZE, buffer);

    //handle = open(port, O_RDWR | O_NONBLOCK | O_NOCTTY);
      handle = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (handle < 0) {
        free(buffer);
        buffer = NULL;
        cout << "Error open the serial port " << port << ":" << strerror(errno) << endl; 
        return;   
    }
    if (ioctl(handle, TIOCEXCL) == -1) {
        cout << "Error setting TIOCEXCL on " << port << ":" << strerror(errno) << endl;
        goto error;
    }
    if (fcntl(handle, F_SETFL, 0) == -1) {
        cout << "Error clearing O_NONBLOCK on " << port << ":" << strerror(errno) << endl;
        goto error;
    }
    struct termios  options;
    if (tcgetattr(handle, &options) == -1) {
        cout << "Error getting tty attribute on " << port << ":" << strerror(errno) << endl;
        goto error;
    }
    
    cfmakeraw(&options);

    cfsetspeed(&options, baudRate);

    options.c_cflag |= (CS8);
    options.c_cc[VTIME] = 1;
    options.c_cc[VMIN] = 0;

    cout << "Uart--" << port << " output baud rate changed to " << cfgetospeed(&options) << endl;

    if (tcsetattr(handle, TCSANOW, &options) == -1) {
        cout << "Error setting tty attribute on " << port << ":" << strerror(errno) << endl;
        goto error;
    }
    cout << "Uart--" << port << "init successful!" << endl;
    
    stop = false;
    if (pthread_create( &uart_id, NULL, uart_thread, (void *)this)) {
        cout << "Error on create thread: " << strerror(errno) << endl;
        goto error;
    }
    cout << "uart thread is runing..." << endl;
    return;

error:
    if (buffer) {
        free(buffer);
        buffer = NULL;
    }
    if (handle >0) {
        close(handle);
        handle = -1;
    }
}

Uart::~Uart()
{
    //pthread_cancel(uart_id);
    stop = true;
    usleep(10000);
    if (buffer) {
        free(buffer);
        buffer = NULL;
    }
    if (handle > 0) {
        close(handle);
        handle = -1;
    }
    if (readBuffer) {
        free(readBuffer);
        readBuffer = NULL;
    }
}

char *Uart::uart_read()
{
    pthread_mutex_lock(&mymutex);
    this->readBuffer = fifo_dump(&fifoBuffer);
    pthread_mutex_unlock(&mymutex);

    return this->readBuffer;
}

bool Uart::wait_forString(const char *waitStr, int timeOut)
{
    if (waitStr == NULL) {
        return false;
    }
    bool retVaule = false;
    CString *cStr = NULL;
    TimeOut *tt = new TimeOut(timeOut);
    while(true) {
        char *s = this->uart_read();
        if (s != NULL) {
            if (cStr == NULL) {
                cStr = Init_CString(s);
            }
            else {
                Append_Chars_To_CString(cStr, s);   
            }
        }
        if (cStr) {
            char *ss = Get_string(cStr);
            if (ss) {
               StrPattern *p = new StrPattern(ss, waitStr);
               if (p->result() == OK) {
                   retVaule = true;
                   break;
               }
               delete p;
            }
        }
        if (tt->isTimeOut()) {
            break;
        }
    }
    Delete_CString(cStr);
    return retVaule;
}

void Uart::send_command(const char *command)
{
    if (command == NULL) {
        return;
    }
    int len = strlen(command) + 3;
    char *cmd = (char *)malloc(len * sizeof(char));
    if (cmd == NULL) {
        printf("Error on malloc: %s\n", strerror(errno));
        return;
    }
    memset(cmd, '\0', len * sizeof(char));
    snprintf(cmd, len, "%s\r\n", command);
    printf("send command:%slen:%d\n", cmd, strlen(cmd));
    int ret = write(handle, cmd, strlen(cmd));
    if (ret < 0) {
        printf("Error on write: %s\n", strerror(errno));
    }
    free(cmd);
    return;
}

void* Uart::uart_thread(void *arg)
{
    Uart *port = (Uart *)arg;
    port->uart_td();
    return NULL;
}

void Uart::uart_td()
{
    int bytes = 0;
    char buf[1024];

    while(true) {
        if (stop) {
            break;
        }
        memset(buf, 0, sizeof(buf));
        bytes = read(handle, buf, sizeof(buf));
        if (bytes > 0) {
            pthread_mutex_lock(&mymutex);
            fifo_add(&fifoBuffer, buf, bytes);
            pthread_mutex_unlock(&mymutex);
        }
        else {
            usleep(1000);
        }
    }
    return;
}

